/**
 * @file Timer.hpp
 *
 *  @date 2009-06-09
 *  @author hauleth
 */

#ifndef TIMER_HPP_
#define TIMER_HPP_

#include <iostream>
#include <string>
#include <ctime>
#include <Bust/Utilities.hpp>

namespace bust {
namespace {

class Timer {
	public:
		Timer() {
			_start_time = std::clock();
		}
		void restart() {
			_start_time = std::clock();
		}
		double elapsed() const {
			return ( ( std::clock() - _start_time ) * 1.0 ) / ( CLOCKS_PER_SEC * 1.0 );
		}
		double elapsed_max() const {
			return ( ( std::numeric_limits< std::clock_t >::max() * 1.0 )
					- ( _start_time * 1.0 ) ) / ( CLOCKS_PER_SEC * 1.0 );
		}
		double elapsed_min() const {
			return  1.0 / CLOCKS_PER_SEC;
		}
	private:
		std::clock_t _start_time;
};

class Benchmark : public Timer {
	public:
		Benchmark( std::ostream& str = std::cout ) : m_os(str) {
		}
		~Benchmark() {
			m_os << '\n' << elapsed() << " s\n" << std::flush;
		}
	private:
		std::ostream& m_os;
};

class ProgressDisplay: private noncopyable {
	public:
		typedef unsigned long tics_t;
		explicit ProgressDisplay( unsigned long expected_count,
				std::ostream & os = std::cout, const std::string & s1 = "\n",
				const std::string & s2 = "", const std::string & s3 = "" ) :
			m_os( os ), m_s1( s1 ), m_s2( s2 ), m_s3( s3 ) {
			restart( expected_count );
		}
		void restart( tics_t expected_count ) {
			_count = _next_tic_count = _tic = 0;
			_expected_count = expected_count;
			m_os << m_s1
					<< "0%   10   20   30   40   50   60   70   80   90   100%\n"
					<< m_s2
					<< "|----|----|----|----|----|----|----|----|----|----|"
					<< std::endl << m_s3;
			if( !_expected_count )
				_expected_count = 1;
		}
		unsigned long operator +=( unsigned long increment ) {
			if( ( _count += increment ) >= _next_tic_count ) {
				display_tic();
			}
			return _count;
		}
		tics_t operator ++() {
			return operator +=( 1 );
		}
		tics_t operator ++( int ) {
			return operator +=( 1 );
		}
		tics_t count() const {
			return _count;
		}
		tics_t expected_count() const {
			return _expected_count;
		}
	private:
		std::ostream & m_os;
		const std::string m_s1;
		const std::string m_s2;
		const std::string m_s3;
		tics_t _count, _expected_count, _next_tic_count;
		unsigned int _tic;
		void display_tic() {
			tics_t tics_needed =
					static_cast< tics_t > ( ( static_cast< double > ( _count )
							/ _expected_count ) * 50.0 );
			do {
				m_os << '*' << std::flush;
			} while( ++_tic < tics_needed );
			_next_tic_count = static_cast< tics_t > ( ( _tic / 50.0 )
					* _expected_count );
			if( _count == _expected_count ) {
				if( _tic < 51 )
					m_os << '*';
				m_os << std::endl;
			}
		}
};

}
}

#endif /* TIMER_HPP_ */
